package com.xt.mvcpilot.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import org.springframework.context.annotation.Scope;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.validation.Validator;  

import com.xt.mvcpilot.AppConstants;
import com.xt.mvcpilot.service.UserService;
import com.xt.mvcpilot.data.User;
import com.xt.mvcpilot.data.Role;
import com.xt.mvcpilot.data.Permission;
import com.xt.mvcpilot.validator.PermissionValidator;
import com.xt.mvcpilot.validator.RoleValidator;
import com.xt.mvcpilot.validator.UserValidator;
import com.xt.mvcpilot.exception.DaoException;

/**
 * 
 * 
 * @author xiaotao
 * @version 2011-6-13
 *
 */
@Controller
@Scope("prototype")
@RequestMapping("/admin")
public class UserController extends AppBasicController{
	
	protected final Logger log = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private UserService userService; //the use service
	
	//data binder
	//1. bind user's role
	//2. bind role's permission
	@InitBinder
	protected void initBinder(HttpServletRequest request, WebDataBinder binder) throws Exception {
		
		//user's role
		binder.registerCustomEditor(Set.class, "roles", new CustomCollectionEditor(Set.class) {
			protected Object convertElement(Object element) {
				if(element instanceof Role){
					return element;
				}
				if(element instanceof String){
					if(!"".equals(element)){
						Long roleId = Long.parseLong((String) element);
						return userService.getRole(roleId);
					}else{
						return null;
					}
				}
				return null;
			}
		});
		
		//role's permission
		binder.registerCustomEditor(Set.class, "perms", new CustomCollectionEditor(Set.class) {
			protected Object convertElement(Object element) {
				if(element instanceof Permission){
					return element;
				}
				if(element instanceof String){
					if(!"".equals(element)){
						Long permissionId = Long.parseLong((String) element);
						return userService.getPermission(permissionId);
					}else{
						return null;
					}
				}
				return null;
			}
		});

		//user's role
//		binder.registerCustomEditor(Set.class, "roles", new CustomCollectionEditor(Set.class){
//          @Override
//          protected Object convertElement(Object element){
//              Long id = null;
//              Role tempRole=null;
//              if(element instanceof String && !((String)element).equals("")){
//                  //From the JSP 'element' will be a String
//                  try{
//                      id = Long.parseLong((String) element);
//                  }catch (NumberFormatException e) {
//                      System.out.println("Element was " + ((String) element));
//                      e.printStackTrace();
//                  }
//              }else if(element instanceof Long) {
//                  //From the database 'element' will be a Long
//                  id = (Long) element;
//              }else if(element instanceof Role){
//            	  tempRole = (Role) element;
//              }
//
//              return id != null ? userService.getRole(id): null;
//          }
//        });
		
		//binder.registerCustomEditor(List.class, "roles", new RoleEditor(this.userService, List.class));
		

	}
	
	/**
	 * the default view of /admin
	 * open the user-overview
	 * 
	 * @return
	 */
	@RequestMapping(value = "/user", method = RequestMethod.GET)
	public ModelAndView overview() {
		Map<String, Object> model = new HashMap<String, Object>();
		model.put(AppConstants.USER_LIST_MODEL, userService.getAll());

		return new ModelAndView (AppConstants.USER_LIST_VIEW, model);
	}
	
	
	/**
	 * open the user overview
	 * TODO pagenation
	 * 
	 * 
	 * @return the user overview
	 */
	@RequestMapping(value = "/user/list", method = RequestMethod.GET)
	public ModelAndView viewUsers() {
		Map<String, Object> model = new HashMap<String, Object>();
		model.put(AppConstants.USER_LIST_MODEL, userService.getAll());

		return new ModelAndView (AppConstants.USER_LIST_VIEW, model);
	}
	
	
	
	/**
	 * open the create-user form
	 * 
	 * @return the overview of the users
	 */
	@RequestMapping(value = "/user/new", method = RequestMethod.GET)
	public ModelAndView newUser() {
		
		Map<String, Object> model = new HashMap<String, Object>();
		model.put(AppConstants.USER_FORM_MODEL, new User());
		
		return new ModelAndView (AppConstants.USER_FORM, model);
	}

	
	@RequestMapping(value = "/user/save", method = RequestMethod.POST)
	//public ModelAndView saveUser(@ModelAttribute("user") User user,
	public ModelAndView saveUser(@Valid @ModelAttribute("user") User user,
		      BindingResult result, Model model){
		try{
//			if(user.getId()==null)
//				new UserValidator().validateCreate(user, result);
//			else
//				new UserValidator().validate(user, result);
//			if(result.hasErrors()){
//				return new ModelAndView(AppConstants.USER_FORM);
//	        }
			
			if(result.hasErrors()){
				return new ModelAndView(AppConstants.USER_FORM);
			}
			
			//userService.saveorupdate(user);
			userService.save(user);
		}catch(Exception e){
			errors.add("error.user.cannotsave");
			errors.add(e.getMessage());
			Map<String, Object> modelLocal = new HashMap<String, Object>();
			modelLocal.put(AppConstants.APPLICATIONERRORMESSAGES, errors);
			return new ModelAndView(AppConstants.USER_FORM,modelLocal);
		}
		
		//return new ModelAndView ("redirect:/"+AppConstants.USER_LIST_VIEW);
		
		Map<String, Object> modelLocal = new HashMap<String, Object>();
		modelLocal.put(AppConstants.APPLICATIONMESSAGES, "user.saved");
		modelLocal.put(AppConstants.USER_FORM_MODEL, user);
		
		return new ModelAndView (AppConstants.USER_FORM, modelLocal);
	}
	
	/**
	 * edit a user by give the restfull link /users/
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping(value = "/user/{id}",method=RequestMethod.GET)
	public ModelAndView openUser(@PathVariable Long id){
		
		Map<String, Object> model = new HashMap<String, Object>();
		try{
			User user = userService.get(id);
			if(user!=null){
				model.put(AppConstants.USER_FORM_MODEL, user);
				return new ModelAndView (AppConstants.USER_FORM, model);
			}else
				errors.add("errors.user.notfound");
		}catch(ObjectRetrievalFailureException orfe){
			errors.add("errors.user.notfound");	
		}catch(Exception e){
			errors.add("errors.user.notfound");
		}
		
		model.put(AppConstants.APPLICATIONERRORMESSAGES, errors);
		return new ModelAndView (AppConstants.APPLICATIONERRORPAGE, model);
		
	}
	
	/**
	 * delete a user
	 * 
	 * @param id the id of the user to be deleted
	 * @return
	 */
	@RequestMapping(value = "/user/delete/{id}",method=RequestMethod.GET)
	public ModelAndView deleteUser(@PathVariable Long id){
		
		try{
			userService.remove(id);
		}catch(Exception e){
			//if error happens, go back to user form
			User user = userService.get(id);
			errors.add("error.user.cannotsave");
			errors.add(e.getMessage());
			Map<String, Object> modelLocal = new HashMap<String, Object>();
			modelLocal.put(AppConstants.APPLICATIONERRORMESSAGES, errors);
			modelLocal.put(AppConstants.USER_FORM_MODEL, user);
			return new ModelAndView(AppConstants.USER_FORM, modelLocal);
		}

		return new ModelAndView("redirect:/"+AppConstants.USER_LIST_VIEW);
		
	}

	
	
	/******  functions to manage role ******/
	@RequestMapping(value = "/role/list", method = RequestMethod.GET)
	public ModelAndView viewRoles() {
		
		return new ModelAndView (AppConstants.ROLE_LIST_VIEW);
		
	}

	
	@RequestMapping(value = "/role/new", method = RequestMethod.GET)
	public ModelAndView newRole() {
	    
	    Map<String, Object> model = new HashMap<String, Object>();
	    model.put(AppConstants.ROLE_FORM_MODEL, new Role());
		
		return new ModelAndView (AppConstants.ROLE_FORM, model);
	}
	
	
	@RequestMapping(value = "/role/save", method = RequestMethod.POST)
	public ModelAndView saveRole( @ModelAttribute("role") Role role,
		      BindingResult result){
		try{
			new RoleValidator().validate(role, result);
	        if(result.hasErrors()) {
	            return new ModelAndView(AppConstants.ROLE_FORM, AppConstants.ROLE_FORM_MODEL, role);
	        }
			userService.saveRole(role);
		}catch(Exception e){
			e.printStackTrace();
			return new ModelAndView(AppConstants.ROLE_FORM, AppConstants.ROLE_FORM_MODEL, role);
		}
		return new ModelAndView ("redirect:/"+AppConstants.ROLE_LIST_VIEW);
	}
	
	@RequestMapping(value = "/role/{id}",method=RequestMethod.GET)
	public ModelAndView openRole(@PathVariable Long id){
		
		Map<String, Object> model = new HashMap<String, Object>();
		model.put(AppConstants.ROLE_FORM_MODEL, userService.getRole(id));
		
		return new ModelAndView (AppConstants.ROLE_FORM, model);
		
	}
	
	
	/**
	 * delete a role
	 * 
	 * @param id the id of the role to be deleted
	 * @return
	 */
	@RequestMapping(value = "/role/delete/{id}",method=RequestMethod.GET)
	public ModelAndView deleterole(@PathVariable Long id){
		
		try{
			userService.deleteRoleById(id);
		}catch(DaoException de){
			
			return new ModelAndView(AppConstants.ROLE_FORM, AppConstants.PERMISSION_FORM_MODEL, userService.getPermission(id));
		}

		return new ModelAndView("redirect:/"+AppConstants.ROLE_LIST_VIEW);
		
	}
	
	
	/******  functions to manage permission ******/
	@RequestMapping(value = "/perm/list", method = RequestMethod.GET)
	public ModelAndView viewPerms() {
		return new ModelAndView(AppConstants.PERMISSION_LIST_VIEW);
	}

	@RequestMapping(value = "/perm/new", method = RequestMethod.GET)
	public ModelAndView newPermission() {
	    return new ModelAndView(AppConstants.PERMISSION_FORM, AppConstants.PERMISSION_FORM_MODEL, new Permission());
	}
	
	
	@RequestMapping(value = "/perm/save", method = RequestMethod.POST)
	public ModelAndView savePermission( @ModelAttribute("permission") Permission perm,
		      BindingResult result){
		try{
			new PermissionValidator().validate(perm, result);
	        if(result.hasErrors()) {
	            return new ModelAndView(AppConstants.PERMISSION_FORM, AppConstants.PERMISSION_FORM_MODEL, perm);
	        }
			userService.savePermission(perm);
		}catch(Exception e){
			//if error happens, go back permission form
			//TODO add error message hier
			return new ModelAndView(AppConstants.PERMISSION_FORM, AppConstants.PERMISSION_FORM_MODEL, perm);
		}
		return new ModelAndView ("redirect:/"+AppConstants.PERMISSION_LIST_VIEW);
	}
	
	
	@RequestMapping(value = "/perm/{id}",method=RequestMethod.GET)
	public ModelAndView openPermission(@PathVariable Long id){
		
		return new ModelAndView (AppConstants.PERMISSION_FORM, AppConstants.PERMISSION_FORM_MODEL, userService.getPermission(id));
		
	}
	
	
	/**
	 * delete a permisson
	 * 
	 * @param id the id of the permission to be deleted
	 * @return
	 */
	@RequestMapping(value = "/perm/delete/{id}", method=RequestMethod.GET)
	public ModelAndView deletePermission(@PathVariable Long id){
		
		try{
			userService.deletePermissionById(id);
		}catch(DaoException de){
			//if error happens, go back permission form
			//show the error message
			Map<String, Object> model = new HashMap<String, Object>();
			model.put(AppConstants.PERMISSION_FORM_MODEL, userService.getPermission(id));
			model.put(AppConstants.FORM_ERRORMODELKEY, "permission.form.error" + ":" + de.getMessage());
			
			return new ModelAndView(AppConstants.PERMISSION_FORM, model);
		}

		return new ModelAndView("redirect:/"+AppConstants.PERMISSION_LIST_VIEW);
		
	}
	
	
	
	/***********  some modelattributes that are used everywhere  ************/
	//Role list
	@ModelAttribute(AppConstants.ROLE_LIST_MODEL)
	public Set<Role> roleList() {
		return userService.getAllRoles();
	}
	
	//Permission list
	@ModelAttribute(AppConstants.PERMISSION_LIST_MODEL)
	public Set<Permission> permsList() {
		try{
			return userService.getAllPermissons();
		}catch(Exception e){
			return null;
		}
	}
	
	//Country list
	@ModelAttribute("countryList")
	public List<String> countryList() {
		String[] countrys = new String[]{"China", "USA", "Germany"};
		return Arrays.asList(countrys);
	}
	

}
